package org.bridgen.runtime;

/**
 * Bridgit is covered by the new BSD License. 
 * You should have received a copy of the new BSD license with Bridgit.
 * 
 * @author Emma Söderberg <emma add cs.lth.se>
 * Copyright (c) 2009
 */

import java.util.ArrayList;

import org.bridgen.runtime.model.AmbNode;
import org.bridgen.runtime.model.Bridge;
import org.bridgen.runtime.model.EOF;
import org.bridgen.runtime.model.Island;
import org.bridgen.runtime.model.LexicalNode;
import org.bridgen.runtime.model.SOF;

public class RecoveryHelper {

	/**
	 * Finds the next unmatched island after the given island
	 * @param island The island to search after
	 * @return The next island or null of there was none
	 */
	public static Island nextUnmatchedStartIsland(Island island) {
		island = nextIsland(island);
		while (island != null) {
			if (island.startOfBridge() && !island.hasBridge()) {
				return island;
			}
			if (island.hasBridge()) {
				Bridge bridge = island.getBridge();
				island = bridge.getEnd();
			}
			island = nextIsland(island);
		}
		return null;
	}
	
	/**
	 * Finds the next unmatched island after the given island with the given tolerance
	 * @param island The island to search after
	 * @param tolerance The tolerance to use
	 * @return The next island or null if there was none
	 */
	public static Island nextUnmatchedIsland(Island island, int tolerance) {
		int tol = 0;
		island = nextIsland(island);
		while (tol <= tolerance && island != null) {
			if (!island.hasBridge()) {
				if (tol == tolerance) {
					return island;
				}
				tol++;
			}
			if (island.startOfBridge() && island.hasBridge()) {
				Bridge bridge = island.getBridge();
				island = bridge.getEnd();
			}
			island = nextIsland(island);
		}
		return null;
	}
	
	public static int nbrOfIslands(SOF sof) {
		int nbr = 1;
		Island island = nextIsland(sof);
		while (island != null) {
			nbr++;
			island = nextIsland(island);
		}
		return nbr;
	}
	

	public static int nbrUnmatchedIslands(SOF sof) {
		int nbr = 0;
		Island island = nextIsland(sof);
		while (island != null) {
			if (!island.hasBridge()) {
				nbr++;
			}
			island = nextIsland(island);
		}
		return nbr;
	}
	
	public static int nbrFakeIslands(SOF sof) {
		int nbr = 0;
		Island island = nextIsland(sof);
		while (island != null) {
			if (island.isFake()) {
				nbr++;
			}
			island = nextIsland(island);
		}
		return nbr;
	}

	

	public static Island nextIsland(LexicalNode node) {
		if (node != null) {
			node = node.getNext();
			while (node != null) {
				if (node instanceof Island) {
					return (Island)node;
				}
				node = node.getNext();
			}
		}
		return null;
	}

	public static Island previousIsland(LexicalNode node) {
		if (node != null) {
			node = node.getPrevious();
			while (node != null) {
				if (node instanceof Island) {
					return (Island)node;
				}
				node = node.getPrevious();
			}
		}
		return null;
	}

	public static void insertBefore(LexicalNode newNode, LexicalNode node) {
		/* DEBUG System.out.println("\tRecovery.insertBefore newNode = " + newNode + ", node = " + node); */
		LexicalNode nodePrev = node.getPrevious();
		if (nodePrev != null) {
			nodePrev.setNext(newNode);
		}
		newNode.setPrevious(nodePrev);
		newNode.setNext(node);
		node.setPrevious(newNode);
	}

	public static void insertAfter(LexicalNode newNode, LexicalNode node) {
		/* DEBUG System.out.println("Recovery.insertAfter newNode = " + newNode + ", node = " + node + 
			"node.getNext(): " + node.getNext()); */
		LexicalNode nodeNext = node.getNext();
		if (nodeNext != null) {
			nodeNext.setPrevious(newNode);
		}
		newNode.setNext(nodeNext);
		newNode.setPrevious(node);
		node.setNext(newNode);
	}

	public static void remove(LexicalNode node) {
		LexicalNode prev = node.getPrevious();
		LexicalNode next = node.getNext();
		if (prev != null) {
			prev.setNext(next);
		}
		if (next != null) {
			next.setPrevious(prev);
		}
	}

	public static SOF copy(SOF sof) {
		SOF sofCopy = (SOF)sof.clone(null);
		LexicalNode newNode = sofCopy;
		LexicalNode node = sof.getNext();
		while (node != null) {
			newNode = node.clone(newNode);
			node = node.getNext();
		}
		return sofCopy;
	}

	public static EOF eof(SOF sof) {
		LexicalNode node = sof;
		while (!((node = node.getNext()) instanceof EOF));
		return (EOF)node;
	}

	
	
	public static StringBuffer printList(SOF sof) {
		StringBuffer buf = new StringBuffer();
		LexicalNode node = sof.getNext();
		int offset = 0;
		while (!(node instanceof EOF)) {
			if (node.includeInPrettyPrint()) {
				String name = node.getClass().getSimpleName();
				String value = node.getValue().replace("\n", "NEWLINE");
				buf.append(name + "(\"" + value + "\")");
			} else {
				buf.append(whiteSpaceOfLength(node.getValue()));
			}
			buf.append("->");
			node.getInterval().pushRight(offset);
			if (node instanceof Island && ((Island)node).isFake()) {
				offset += node.getValue().length();
			}
			node = node.getNext();
		}
		return buf;		
	}
	
	public static void printList(LexicalNode node, StringBuffer buf, String indent) {
		while (node != null) {
			//String name = node.getClass().getSimpleName();
			//String value = node.getValue().replace("\n", "NEWLINE");
			//buf.append(indent + "#" + name + "(\"" + value + "\")\n");
			buf.append(indent + "#" + node + "\n");
			if (node instanceof AmbNode) {
				ArrayList<LexicalNode> altList = ((AmbNode)node).nextOptions();
				int altNbr = 1;
				for (LexicalNode altNode : altList) {
					buf.append(altNbr++ + ": ");
					printList(altNode, buf, "\t" + indent);
				}
				//buf.append("\n\t");
			}
			node = node.getNext();
		}
	}

	
	public static StringBuffer prettyPrint(SOF sof) {
		StringBuffer buf = new StringBuffer();
		LexicalNode node = sof.getNext();
		int offset = 0;
		while (!(node instanceof EOF)) {
			if (node.includeInPrettyPrint()) {
				buf.append(node.getValue());
			} else {
				buf.append(whiteSpaceOfLength(node.getValue()));
			}
			node.getInterval().pushRight(offset);
			if (node instanceof Island && ((Island)node).isFake()) {
				offset += node.getValue().length();
			}
			node = node.getNext();
		}
		return buf;
	}

	private static String whiteSpaceOfLength(String value) {
		StringBuffer buf = new StringBuffer();
		for (int i = 0; i < value.length(); i++) {
			if (value.charAt(i) == '\n') {
				buf.append("\n");
			} else {
				buf.append(" ");
			}
		}
		return buf.toString();
	}

	public static void printStats(SOF sof, String content, String sample) {
		System.out.println("-- Statistics for " + sample + " --");
		int nbrLines = 0;
		for (int i = 0; i < content.length(); i++) {
			if (content.charAt(i) == '\n') {
				nbrLines++;
			}
		}
		System.out.println("Number of lines: " + nbrLines);
		int nbrIslands = 0;
		int nbrNodes = 0;
		LexicalNode node = sof.getNext();
		while (!(node instanceof EOF)) {
			nbrNodes++;
			if (node instanceof Island) {
				nbrIslands++;
			}
			node = node.getNext();
		}
		System.out.println("Number of nodes: " + nbrNodes);
		System.out.println("Number of islands: " + nbrIslands);
		int maxDepth = 0;
		int depth = 0;
		Island island = nextIsland(sof);
		while (!(island instanceof EOF)) {
			if (island.hasBridge()) {
				if (island.startOfBridge()) {
					depth++;
					if (depth > maxDepth) {
						maxDepth = depth;
					}
				} else {
					depth--;
				}
			}
			island = nextIsland(island);
		}
		System.out.println("Maximum nesting depth: " + maxDepth);
		System.out.println("--");
	}
	

	
	public static class Metrics {
		// Total tolerance used to build bridges
		private int BBT = 0;
		// Number of unmatched islands
		private int BBU = 0;
		// Number of unmatched repairs
		private int BRU = 0;
		
		public int getBBT() {
			return BBT;
		}
		public int getBBU() {
			return BBU;
		}
		public int getBRU() {
			return BRU;
		}
		public int getCombinedValue() {
			return BBT + BBU + BRU;
		}
		public void print() {
			System.out.println("Metrics value: " + getCombinedValue() + "\n" +
					"\tBBT = " + BBT + "\n" + 
					"\tBBU = " + BBU + "\n" + 
					"\tBRU = " + BRU);
		}
		public void clear() {
			BBT = 0;
			BBU = 0;
			BRU = 0;
		}
	}
	
	public static void collectBBT(Bridge b, Metrics m) {
		if (b == null)
			return;
		m.BBT += b.getTolerance();
		Island island = nextIsland(b.getStart());
		while (island != b.getEnd()) {
			if (island.hasBridge()) {
				collectBBT(island.getBridge(), m);
			}
			island = nextIsland(island);
		}
	}
	
	public static void collectBBU(SOF sof, Metrics m) {
		m.BBU = 0;
		Island island = nextIsland(sof);
		while (island != null) {
			if (!island.hasBridge()) {
				m.BBU++;
			}
			island = nextIsland(island);
		}
	}
	
	public static void collectBRU(SOF sof, Metrics m) {
		m.BRU = 0;
		Island island = nextIsland(sof);
		while (island != null) {
			if (island.isFake()) {
				m.BRU++;
			}
			island = nextIsland(island);
		}
	}
}